/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun<lingyun@email.com>
 *                  All rights reserved.
 *
 *       Filename:  logger.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(13/03/25)
 *         Author:  LingYun <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "13/03/25 21:19:19"
 *                 
 ********************************************************************************/
#include "logger.h"

/*
 *   Program name variable is provided by the libc
 */
extern const char* __progname;
#define PROGRAM_NAME __progname

/*
 *   Logger internal sctructure
 */
typedef struct logger_s {
	FILE    *fp;
	int     loglevel;
	int     use_stdout;
	size_t  max_size;
	char    *filename;
} logger_t;

static struct logger_s g_logger;

static const char* LOG_LEVELS[] = {    /* 定义四种等级 */
	LOG_STRING_ERROR,
	LOG_STRING_WARN,
	LOG_STRING_INFO,
	LOG_STRING_DEBUG
};

/*
 *   initial logger system
 */
int logger_init(char *filename, int loglevel)       /* filename文件名 loglevel记录要设置级别 */
{
	logger_term();     /*先关闭一下日志系统*/

	g_logger.loglevel = loglevel>LOG_LEVEL_MAX ? LOG_LEVEL_MAX : loglevel;    /*判断当前等级是不是比最高级高 */

	if( !filename || !strcasecmp(filename, "stdout"))   /*它会根据传入的文件名filename来决定是将日志输出到标准输出还是指定文件
														  如果指针为空或者文件是一个标准输出stdout的权限 */
	{
		g_logger.use_stdout = 1;     /* 直接做标准输出使用，标准输出是1 */
		g_logger.fp = stderr;        /* 设置fp为标准输出 */
	}
	else
	{
		g_logger.use_stdout = 0;   /* 否则就输出到文件中 */
		g_logger.fp = fopen(filename, "a");    /* 打开要输出的文件，“a”表示以追加的方式写在后面 */
		if( !g_logger.fp )    /*如果fp=NULL,说明打开文件失败*/
		{
			fprintf(stderr, "Failed to open file '%s': %s", filename, strerror(errno));
			return -1;
		}
	}

	return 0;
}

/*
 *   terminate logger system
 */
void logger_term(void)      /* 上面打开了日志系统，就一定要关闭，这个函数用来关闭日志文件 */
{
	if( !g_logger.fp )    /*fp==NULL,说明是标准输出*/
	{
		return ;
	}

	if( !g_logger.use_stdout )   /*use_stdout==NULL,说明是输出到日志文件中的，就关闭日志文件 */
	{
		fclose(g_logger.fp);
	}

	g_logger.use_stdout = 0;
	g_logger.fp = NULL;

	return ;
}

/*
 *   Logging functions
 */
void log_generic(const int level, const char* format, va_list args)    /*定义日志的输出格式 */
{
	char         message[256];
	struct tm*   current_tm;
	time_t       time_now;
	
	vsnprintf(message, sizeof(message), format, args);

	time(&time_now);
	current_tm = localtime(&time_now);

	int res = fprintf(g_logger.fp,
			"%s : %02i:%02i:%02i [%s] %s\n"
			, PROGRAM_NAME
			, current_tm->tm_hour
			, current_tm->tm_min
			, current_tm->tm_sec
			, LOG_LEVELS[level]
			, message );
	if(res < 0)
	{
		printf("log write failed\n");
		if(g_logger.fp)
		{
			fclose(g_logger.fp);
			g_logger.fp = NULL;
		}
	}
	logger_rollback();
	fflush(g_logger.fp);     /* fflush函数就是强制将缓冲区中的数据写入到对应的文件或者设备中，或者清空输入缓冲区 */
}

void log_error(char *format, ...)    /*最高级别，一定会记录*/
{
	va_list args;    
	va_start(args, format);  
	log_generic(LOG_LEVEL_ERROR, format, args);
	va_end(args);
}

void log_warn(char *format, ...)
{
	if (g_logger.loglevel < LOG_LEVEL_WARN) {    /*判断当前级别有没有warn的级别高，比warn级别低就不打印 */
		return;
	}

	va_list args;
	va_start(args, format);
	log_generic(LOG_LEVEL_WARN, format, args);
	va_end(args);
}

void log_info(char *format, ...)
{
	if (g_logger.loglevel < LOG_LEVEL_INFO) {      /* 判断当前级别有没有warn的级别高，比info级别低就不打印 */
		return;
	}

	va_list args;
	va_start(args, format);
	log_generic(LOG_LEVEL_INFO, format, args);
	va_end(args);
}

void log_debug(char *format, ...)
{
	if (g_logger.loglevel < LOG_LEVEL_DEBUG) {    /* 判断当前级别有没有warn的级别高，比degub级别低就不打印 */
		return;
	}

	va_list args;
	va_start(args, format);
	log_generic(LOG_LEVEL_DEBUG, format, args);
	va_end(args);
}

/*日志回滚*/
int logger_rollback() 
{
	if (g_logger.fp == NULL || g_logger.filename == NULL) 
	{
		return -1; // 日志文件未打开或文件名无效
	}

	time_t now = time(NULL);
	struct tm *tm_info = localtime(&now);

	char timestamp[20];
	strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", tm_info);

	char new_filename[100];
	snprintf(new_filename, sizeof(new_filename), "log_%s.log", timestamp);

	// 打开新的日志文件
	g_logger.fp = fopen(new_filename, "a");
	if (g_logger.fp == NULL)
	{
		perror("fopen");
		return -1;
	}
	// 更新日志系统中的文件名
	free(g_logger.filename);
	g_logger.filename = strdup(new_filename);

	return 0;
}
